#include "SpriteImage.h"

/*!
 * \fn N2F::Iw3D::SpriteImage::AnimInfo::AnimInfo()
 *
 * \brief Default constructor for private AnimInfo struct.
 *
 * \author Andrew Male
 * \date 9/2/2013
 */
N2F::Iw3D::SpriteImage::AnimInfo::AnimInfo()
{
	this->startFrame = -1;
	this->endFrame = -1;
	this->duration = -1;

	return;
}

/*!
 * \fn N2F::Iw3D::SpriteImage::AnimInfo::AnimInfo(int startFrame, int endFrame, int64 duration)
 *
 * \brief Constructor for private AnimInfo struct.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param startFrame The start frame number.
 * \param endFrame The end frame number.
 * \param duration The duration in milliseconds.
 */
N2F::Iw3D::SpriteImage::AnimInfo::AnimInfo(int startFrame, int endFrame, int64 duration)
{
	this->startFrame = startFrame;
	this->endFrame = endFrame;
	this->duration = duration;

	return;
}

/*!
 * \fn N2F::Iw3D::SpriteImage::AnimTracker::AnimTracker(int64 start, int index)
 *
 * \brief Constructor for private AnimTracker struct.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param start The start delta time in milliseconds.
 * \param index Zero-based index of the animation.
 */
N2F::Iw3D::SpriteImage::AnimTracker::AnimTracker(int64 start, int index)
{
	this->start = start;
	this->index = index;
	this->frame = 0;

	return;
}

/*!
 * \fn N2F::Iw3D::SpriteImage::SpriteImage(const ScaleFactor &scaleFactor)
 *
 * \brief Constructor for the SpriteImage base element.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param scaleFactor The scale factor.
 */
N2F::Iw3D::SpriteImage::SpriteImage(const ScaleFactor &scaleFactor)
	: Element(scaleFactor), animProgress(-1, -1)
{
	this->numCols = 0;
	this->numRows = 0;
	this->numFrames = 0;
	this->currentAnimation = -1;
	this->idleFrame = -1;

	this->cellWidth = 0.0f;
	this->cellHeight = 0.0f;
	this->cellHorizontalPadding = 0.0f;
	this->cellVerticalPadding = 0.0f;

	this->animProgress = AnimTracker(-1, -1);
	this->hitBox = NULL;
	this->img = NULL;

	return;
}

/*!
 * \fn N2F::Iw3D::SpriteImage::~SpriteImage()
 *
 * \brief Destructor for the SpriteImage base element.
 *
 * \author Andrew Male
 * \date 9/2/2013
 */
N2F::Iw3D::SpriteImage::~SpriteImage()
{
	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::setFile(const char *file)
 *
 * \brief Sets the file for the spritesheet and loads it as an image internally.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param file The path to the spritesheet file.
 */
void N2F::Iw3D::SpriteImage::setFile(const char *file)
{
	this->file = file;
	this->img = Iw2DCreateImage(file);
	this->img->GetMaterial()->SetFiltering(false);
	this->size = CIwFVec2(this->img->GetWidth(), this->img->GetHeight());
	this->hitBox = new Rectangle(this->pos, this->size);

	return;
}

/*!
 * \fn const char *N2F::Iw3D::SpriteImage::getFile() const
 *
 * \brief Gets the file path for the spritesheet.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \return null if it fails, else the file.
 */
const char *N2F::Iw3D::SpriteImage::getFile() const
{
	return(this->file);
}

/*!
 * \fn const float N2F::Iw3D::SpriteImage::getCellWidth() const
 *
 * \brief Gets cell width.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \return The cell width.
 */
const float N2F::Iw3D::SpriteImage::getCellWidth() const
{
	return(this->cellWidth);
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::setCellWidth(float width)
 *
 * \brief Sets cell width.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \param width The width.
 */
void N2F::Iw3D::SpriteImage::setCellWidth(float width)
{
	this->cellWidth = width;

	if (this->cellHeight > 0.0f && this->cellWidth > 0.0f)
	{
		this->numCols = (int)(this->size.x / this->cellWidth);
		this->numRows = (int)(this->size.y / this->cellHeight);
		this->numFrames = this->numCols * this->numRows;
	}

	return;
}

/*!
 * \fn const float N2F::Iw3D::SpriteImage::getCellHeight() const
 *
 * \brief Gets cell height.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \return The cell height.
 */
const float N2F::Iw3D::SpriteImage::getCellHeight() const
{
	return(this->cellHeight);
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::setCellHeight(float height)
 *
 * \brief Sets cell height.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \param height The height.
 */
void N2F::Iw3D::SpriteImage::setCellHeight(float height)
{
	this->cellHeight = height;

	if (this->cellWidth > 0.0f && this->cellHeight > 0.0f)
	{
		this->numCols = (int)(this->size.x / this->cellWidth);
		this->numRows = (int)(this->size.y / this->cellHeight);
		this->numFrames = this->numCols * this->numRows;
	}

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::setIdleFrame(int frame)
 *
 * \brief Sets idle frame.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \param frame The frame.
 */
void N2F::Iw3D::SpriteImage::setIdleFrame(int frame)
{
	this->idleFrame = frame;

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::setAnimation(int index, int startFrame, int endFrame,
 *     int64 duration)
 *
 * \brief Sets an animation on the spritesheet.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \param index	 Zero-based index of the animation.
 * \param startFrame The start frame index.
 * \param endFrame   The end frame index.
 * \param duration   The duration.
 */
void N2F::Iw3D::SpriteImage::setAnimation(int index, int startFrame, int endFrame, int64 duration)
{
	if (!this->animations.empty())
	{
		for (AnimListIter it = this->animations.begin(); it != this->animations.end(); ++it)
		{
			if ((*it).first == index)
			{
				(*it).second.startFrame = startFrame;
				(*it).second.endFrame = endFrame;
				(*it).second.duration = duration;

				return;
			}
		}
	}

	this->animations.insert(AnimListItem(index, AnimInfo(startFrame, endFrame, duration)));

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::triggerAnimation(int index)
 *
 * \brief Trigger animation.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param index Zero-based index of the.
 */
void N2F::Iw3D::SpriteImage::triggerAnimation(int index)
{
	if (this->animations.empty() || index > (int)this->animations.size())
	{
		return;
	}

	for (AnimListIter it = this->animations.begin(); it != this->animations.end(); ++it)
	{
		if ((*it).first == index)
		{
			this->currentAnimation = index;
			this->animProgress = AnimTracker(s3eTimerGetUST(), index);

			return;
		}
	}

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::stopAnimations()
 *
 * \brief Stops the currently running animation.
 *
 * \author Andrew Male
 * \date 9/2/2013
 */
void N2F::Iw3D::SpriteImage::stopAnimations()
{
	this->currentAnimation = -1;
	this->animProgress = AnimTracker(0, -1);

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::updateAnimation(int64 delta)
 *
 * \brief Updates the animation described by delta.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param delta The delta time.
 */
void N2F::Iw3D::SpriteImage::updateAnimation(int64 delta)
{
	if (this->currentAnimation < 0)
	{
		return;
	}

	AnimInfo anim = this->animations[this->currentAnimation];
	int numFrames = anim.endFrame - anim.startFrame;
	int msPerFrame = (numFrames == 0) ? (int)anim.duration : ((int)anim.duration / numFrames);
	int timePassed = (int)delta - (int)this->animProgress.start;

	if (timePassed > ((int)anim.duration - msPerFrame))
	{
		this->animProgress.frame = anim.endFrame;

		return;
	}

	if (timePassed <= msPerFrame)
	{
		this->animProgress.frame = anim.startFrame;

		return;
	}

	this->animProgress.frame = anim.startFrame + (((int)floor(timePassed / msPerFrame)) - 1);

	return;
}

/*!
 * \fn bool N2F::Iw3D::SpriteImage::animComplete()
 *
 * \brief Determines if any playing animation is complete.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \return true if the animation is completed or there isn't an animation playing, false if the animation is still ongoing.
 */
bool N2F::Iw3D::SpriteImage::animComplete()
{
	if (this->currentAnimation < 0)
	{
		return(true);
	}

	AnimInfo anim = this->animations[this->currentAnimation];

	if (anim.startFrame == anim.endFrame)
	{
		if ((s3eTimerGetUST() - this->animProgress.start) > anim.duration)
		{
			return(true);
		}

		return(false);
	}

	if (this->animProgress.frame == anim.endFrame)
	{
		return(true);
	}

	return(false);
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::draw(int64 delta)
 *
 * \brief Draws the current frame only using it's internal positioning and sizing.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param delta The last update delta time.
 */
void N2F::Iw3D::SpriteImage::draw(int64 delta)
{
	int drawFrame = this->getDrawFrame();
	CIwFVec2 region = this->getFrameRegion(drawFrame);
	CIwFVec2 rgsize = this->getFrameSize();

	CIwFVec2 s = this->translateSize();
	CIwFVec2 p = this->translatePosition();

	Iw2DDrawImageRegion(this->img, p, s, region, rgsize);

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::draw(int64 delta, CIwFVec2 offset)
 *
 * \brief Draws the current frame using a supplied offset.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param delta  The last update delta time.
 * \param offset The x/y offset to use in addition to the internal scaleFactor.
 */
void N2F::Iw3D::SpriteImage::draw(int64 delta, CIwFVec2 offset)
{
	int drawFrame = this->getDrawFrame();
	CIwFVec2 region = this->getFrameRegion(drawFrame);
	CIwFVec2 rgsize = this->getFrameSize();

	CIwFVec2 s = this->translateSize();
	CIwFVec2 p = this->translatePosition();
	p.x += (offset.x * this->scaleFactor.scale);
	p.y += (offset.y * this->scaleFactor.scale);

	Iw2DDrawImageRegion(this->img, p, s, region, rgsize);

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::draw(int64 delta, CIwFVec2 offset, float angle)
 *
 * \brief Draws the current frame using a supplied offset and rotation angle.
 *
 * \author Andrew Male
 * \date 9/2/2013
 * 
 * \attention This is an example of an attention block.
 * 		    Use it well.
 *
 * \param delta  The last update delta time.
 * \param offset The x/y offset to use in addition to the internal \ref scaleFactor.
 * \param angle  The rotation angle to use for display.
 */
void N2F::Iw3D::SpriteImage::draw(int64 delta, CIwFVec2 offset, float angle)
{
	int drawFrame = this->getDrawFrame();
	CIwFVec2 region = this->getFrameRegion(drawFrame);
	CIwFVec2 rgsize = this->getFrameSize();

	CIwFVec2 s = this->translateSize();
	CIwFVec2 p = this->translatePosition();
	p.x += (offset.x * this->scaleFactor.scale);
	p.y += (offset.y * this->scaleFactor.scale);

	CIwFMat2D m = CIwFMat2D::g_Identity;
	m.Scale(this->scaleFactor.scale);
	m.SetTrans(CIwFVec2((p.x + (s.x / 2)), (p.y + (s.y / 2))));

	Iw2DSetTransformMatrix(m);
	Iw2DDrawImageRegion(this->img, p, region, rgsize);
	Iw2DSetTransformMatrix(CIwFMat2D::g_Identity);

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::draw(int64 delta, CIwFVec2 offset, float angle, float scale)
 *
 * \brief Draws the current frame using a supplied offset, rotation angle, and scale modifier.
 *
 * \author Andrew Male
 * \date 9/2/2013
 *
 * \param delta  The last update delta time.
 * \param offset The x/y offset to use in addition to the internal scaleFactor.
 * \param angle  The rotation angle to use for display.
 * \param scale  The scale modifier to adjust displayed size.
 */
void N2F::Iw3D::SpriteImage::draw(int64 delta, CIwFVec2 offset, float angle, float scale)
{
	int drawFrame = this->getDrawFrame();
	CIwFVec2 region = this->getFrameRegion(drawFrame);
	CIwFVec2 rgsize = this->getFrameSize();

	CIwFVec2 p = CIwFVec2(
		((this->pos.x * this->scaleFactor.scale) + this->scaleFactor.offset.x + offset.x),
		((this->pos.y * this->scaleFactor.scale) + this->scaleFactor.offset.y + offset.y)
	);
	CIwFVec2 s = CIwFVec2(
		(this->size.x * scale),
		(this->size.y * scale)
	);

	CIwFMat2D m = CIwFMat2D::g_Identity;
	m.Scale(this->scaleFactor.scale);
	m.SetTrans(CIwFVec2((p.x + (s.x / 2)), (p.y + (s.y / 2))));

	Iw2DSetTransformMatrix(m);
	Iw2DDrawImageRegion(this->img, p, region, rgsize);
	Iw2DSetTransformMatrix(CIwFMat2D::g_Identity);

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::OnKill()
 *
 * \brief Executes the kill action.
 *
 * \author Andrew Male
 * \date 9/3/2013
 */
void N2F::Iw3D::SpriteImage::OnKill()
{
	if (this->hitBox != NULL)
	{
		delete this->hitBox;
		this->hitBox = NULL;
	}

	if (this->img != NULL)
	{
		delete this->img;
		this->img = NULL;
	}

	return;
}

/*!
 * \fn void N2F::Iw3D::SpriteImage::OnTouch(InputPoint point)
 *
 * \brief Executes the touch action.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \param point The point.
 */
void N2F::Iw3D::SpriteImage::OnTouch(InputPoint point)
{
	return;
}

/*!
 * \fn bool N2F::Iw3D::SpriteImage::isHit(CIwFVec2 point)
 *
 * \brief Query if 'point' is hit.
 *
 * \author Andrew Male
 * \date 9/3/2013
 *
 * \param point The point.
 *
 * \return true if hit, false if not.
 */
bool N2F::Iw3D::SpriteImage::isHit(CIwFVec2 point)
{
	return(false);
}

/*!
 * \fn CIwFVec2 N2F::Iw3D::SpriteImage::translateSize()
 *
 * \brief Gets the translated cell size (the size of what is rendered).
 *
 * \author Andrew Male
 * \date 9/5/2013
 *
 * \return CIwFVec2 of scaled cell size.
 */
CIwFVec2 N2F::Iw3D::SpriteImage::translateSize()
{
	CIwFVec2 ret;
	ret.x = this->cellWidth * this->scaleFactor.scale;
	ret.y = this->cellHeight * this->scaleFactor.scale;

	return(ret);
}

/*!
 * \fn CIwFVec2 N2F::Iw3D::SpriteImage::translateSize(float scale)
 *
 * \brief Gets the translated cell size (the size of what is rendered) with a new scale factor.
 *
 * \author Andrew Male
 * \date 9/5/2013
 *
 * \param scale The scale factor.
 *
 * \return CIwFVec2 of scaled cell size.
 */
CIwFVec2 N2F::Iw3D::SpriteImage::translateSize(float scale)
{
	CIwFVec2 ret;
	ret.x = this->cellWidth * scale;
	ret.y = this->cellHeight * scale;

	return(ret);
}

/*!
 * \fn CIwFVec2 N2F::Iw3D::SpriteImage::translatePosition()
 *
 * \brief Gets the translated position, adjusted to be based on cell size.
 *
 * \author Andrew Male
 * \date 9/5/2013
 *
 * \return CIwFVec2 of scaled position.
 */
CIwFVec2 N2F::Iw3D::SpriteImage::translatePosition()
{
	return(this->translateVector(this->pos, CIwFVec2(this->cellWidth, this->cellHeight), this->originPoint, this->scaleGroup));
}

/*!
 * \fn CIwFVec2 N2F::Iw3D::SpriteImage::translatePosition(float scale)
 *
 * \brief Gets the translated position, adjusted to be based on cell size and with a new scale factor.
 *
 * \author Andrew Male
 * \date 9/5/2013
 *
 * \param scale The scale factor.
 *
 * \return CIwFVec2 of scaled position.
 */
CIwFVec2 N2F::Iw3D::SpriteImage::translatePosition(float scale)
{
	return(this->translateVector(this->pos, CIwFVec2(this->cellWidth, this->cellHeight), scale, this->originPoint, this->scaleGroup));
}

/*!
 * \fn int N2F::Iw3D::SpriteImage::getDrawFrame()
 *
 * \brief Gets index of the frame to draw.
 *
 * \author Andrew Male
 * \date 9/16/2013
 *
 * \return The draw frame index.
 */
int N2F::Iw3D::SpriteImage::getDrawFrame()
{
	int drawFrame = -1;

	if (this->currentAnimation < 0)
	{
		drawFrame = this->idleFrame;
	}
	else
	{
		drawFrame = this->animProgress.frame;
	}

	return(drawFrame);
}

/*!
 * \fn CIwFVec2 N2F::Iw3D::SpriteImage::getFrameRegion()
 *
 * \brief Gets frame region.
 *
 * \author Andrew Male
 * \date 9/16/2013
 *
 * \return The frame region.
 */
CIwFVec2 N2F::Iw3D::SpriteImage::getFrameRegion(int drawFrame)
{
	int colNum = drawFrame % this->numCols;
	int rowNum = (int)floor(drawFrame / this->numCols);

	return(CIwFVec2(((this->cellWidth * colNum) + this->cellHorizontalPadding), ((this->cellHeight * rowNum) + this->cellVerticalPadding)));
}

/*!
 * \fn CIwFVec2 N2F::Iw3D::SpriteImage::getFrameSize()
 *
 * \brief Gets frame size.
 *
 * \author Andrew Male
 * \date 9/16/2013
 *
 * \return The frame size.
 */
CIwFVec2 N2F::Iw3D::SpriteImage::getFrameSize()
{
	return(CIwFVec2((this->cellWidth - (this->cellHorizontalPadding * 2)), (this->cellHeight + (this->cellVerticalPadding * 2))));
}
